/*
 * AudioEncoder.cpp
 *
 *  Created on: 2015年12月31日
 *      Author: terry
 */

#include "AudioEncoder.h"
#include <assert.h>
#include "DateTime.h"


namespace av
{



AudioEncoder::AudioEncoder():
		m_context(),
		m_swrContext(),
		m_fifo(),
        m_sampleCount()
{
	
}

AudioEncoder::~AudioEncoder()
{
	close();
}

int AudioEncoder::open(const MediaProfile& profile)
{
	m_profile = profile;

	int rc = 0;
	if (m_profile.checkAudio())
	{
		rc = openEncoder(m_profile);
	}
	return rc;
}

void AudioEncoder::close()
{
	closeEncoder();

	closeFifo();

    m_sampleCount = 0;
}

bool AudioEncoder::isOpen()
{
	return (m_context != NULL);
}

int AudioEncoder::encode(AVFrame* frame, AVPacket* pkt)
{
	int rc = 0;
	if (!isEncoderOpen())
	{
		m_profile.m_channels = frame->channels;
		m_profile.m_samplerate = frame->sample_rate;

		rc = openEncoder(m_profile);
		if (rc != 0)
		{
			return rc;
		}
	}

	if (frame)
	{
		if (isSamleFormat(frame->format, frame->channels, frame->sample_rate))
		{
            storeFrame(frame);
        }
        else
        {
			/// resample
            AVFramePtr outFrame(av_frame_alloc(), avframe_free);

            outFrame->format = m_context->sample_fmt;
            outFrame->channels = m_context->channels;
            outFrame->channel_layout = m_context->channel_layout;
            outFrame->sample_rate = m_context->sample_rate;
            outFrame->pts = frame->pts;

			rc = resample(frame, outFrame.get());
			if (rc > 0)
			{
                storeFrame(outFrame.get());
			}
			else
			{
				return rc;
			}
		}

		
	}

	if (getFifoSize() < m_context->frame_size)
	{
		return ENOMEM;
	}

	return doEncode(pkt);

}

void AudioEncoder::flush()
{
	// pass
}

bool AudioEncoder::getMediaFormat(MediaFormat& fmt)
{
	if (!m_profile.checkAudio())
	{
		return false;
	}

	fmt.m_audioCodec = (av::MediaCodec)m_profile.m_audioCodec;
	fmt.m_channels = m_profile.m_channels;
	fmt.m_sampleRate = m_profile.m_samplerate;
	fmt.m_audioBitrate = m_profile.m_audioBitrate;
	fmt.m_audioProfile = m_profile.m_audioProfile;
	std::string config(fmt.m_audioConfig.c_str(), fmt.m_audioConfig.size());
	getAudioConfig(config);

	return true;
}

int AudioEncoder::openEncoder(const MediaProfile& profile)
{
    AVCodecID codecID = (AVCodecID)profile.m_audioCodec;
	AVCodec* codec = avcodec_find_encoder(codecID);
	if (!codec)
	{
		return ENOENT;
	}

	m_context = avcodec_alloc_context3(codec);
	if (!m_context)
	{
		return ENOENT;
	}

    if (codecID == AV_CODEC_ID_AAC) 
    {
	    m_context->profile = FF_PROFILE_AAC_LOW; //profile.m_audioProfile;
    	//m_context->level = profile.m_audioLevel;
    }

	m_context->channels = profile.m_channels;
    m_context->channel_layout = av_get_default_channel_layout(profile.m_channels);
	m_context->sample_rate = profile.m_samplerate;
    m_context->sample_fmt = codec->sample_fmts[0];
    m_context->time_base = av_make_q(1, profile.m_samplerate);

    m_context->flags |= CODEC_FLAG2_LOCAL_HEADER;

    AVDictionary* options = NULL;
    if (codecID == AV_CODEC_ID_AAC)
    {
        av_dict_set(&options, "strict", "-2", 0);
    }

    int rc = avcodec_open2(m_context, codec, &options);

    av_dict_free(&options);

    if (rc < 0)
    {
        char buf[256];
        av_strerror(rc, buf, 256);

        av_free(m_context);
        m_context = NULL;

        return ENOENT;
    }

    if (m_context->extradata_size > 0)
    {
        m_extradata.assign((char*)m_context->extradata, m_context->extradata_size);
    }

	return 0;
}

void AudioEncoder::closeEncoder()
{
	if (m_swrContext)
	{
		swr_close(m_swrContext);
		swr_free(&m_swrContext);
		m_swrContext = NULL;
	}

	if (m_context)
	{
		avcodec_close(m_context);
		av_free(m_context);
		m_context = NULL;
	}
}

bool AudioEncoder::isEncoderOpen()
{
	return (m_context != NULL);
}

bool AudioEncoder::isSamleFormat(int fmt, int channels, int sampleRate)
{
	return (m_profile.m_channels == channels) && 
        (m_profile.m_samplerate == sampleRate) && 
        (fmt == m_context->sample_fmt);
}

bool AudioEncoder::getAudioConfig(std::string& config)
{
    config = m_extradata;
	return true;
}

int AudioEncoder::resample(AVFrame* inFrame, AVFrame* outFrame)
{
    if (m_swrContext == NULL)
    {
        m_swrContext = swr_alloc();

        swr_config_frame(m_swrContext, outFrame, inFrame);

        swr_init(m_swrContext);
    }
    else
    {
        int ret = swr_config_frame(m_swrContext, outFrame, inFrame);
    }

    int ret = swr_convert_frame(m_swrContext, outFrame, inFrame);

    return outFrame->nb_samples;
}

void AudioEncoder::storeFrame(AVFrame* frame)
{
	if (!m_fifo)
	{
		m_fifo = av_audio_fifo_alloc((AVSampleFormat)frame->format, frame->channels, frame->nb_samples);
	}
    
    av_audio_fifo_write(m_fifo, (void**)frame->data, frame->nb_samples);
}

bool AudioEncoder::loadFrame(AVFrame* frame, int nb_samples)
{
	if (!m_fifo)
	{
		return false;
	}

	if (av_audio_fifo_size(m_fifo) < nb_samples)
	{
		return false;
	}

	av_audio_fifo_read(m_fifo, (void**)frame->data, nb_samples);
	return true;
}

void AudioEncoder::closeFifo()
{
	if (m_fifo)
	{
		av_audio_fifo_free(m_fifo);
		m_fifo = NULL;
	}
}

int AudioEncoder::getFifoSize()
{
	if (!m_fifo)
	{
		return 0;
	}
	return av_audio_fifo_size(m_fifo);
}

int AudioEncoder::doEncode(AVPacket* pkt)
{
	AVFramePtr encFrame(av_frame_alloc(), avframe_free);
	AVFrame* frame = encFrame.get();
	frame->nb_samples = m_context->frame_size;
	frame->channels = m_context->channels;
	frame->channel_layout = m_context->channel_layout;
	frame->sample_rate = m_context->sample_rate;
	frame->format = m_context->sample_fmt;

	av_frame_get_buffer(frame, 0);

	if (!loadFrame(encFrame.get(), m_context->frame_size))
	{
		return ENOMEM;
	}

    m_sampleCount += m_context->frame_size;
    frame->pts = m_sampleCount;

	int got_packet = 0;
	int rc = avcodec_encode_audio2(m_context, pkt, frame, &got_packet);
	if (rc == 0)
	{
        if (got_packet)
        {
            pkt->duration = frame->nb_samples;
            pkt->pos = comn::DateTime::now().totalMillisecond();
        }

		rc = got_packet ? 0 : EAGAIN;
	}

	return rc;
}

bool AudioEncoder::getInputFormat(int& pixelFormat, int& sampleFormat)
{
	sampleFormat = AV_SAMPLE_FMT_NONE;
	return true;
}

void AudioEncoder::enableAccel(bool enabled)
{
	///
}

bool AudioEncoder::setParam(const std::string& name, const char* value)
{
	return true;
}



} /* namespace av */
